package com.seepine.wechat.service;

import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.Padding;
import cn.hutool.crypto.symmetric.AES;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.seepine.wechat.WeChatProperties;
import com.seepine.wechat.constant.ApiConstant;
import com.seepine.wechat.constant.ErrorConstant;
import com.seepine.wechat.constant.KeyConstant;
import com.seepine.wechat.entity.*;
import com.seepine.wechat.exception.WeChatException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.Base64Utils;

/**
 * @author Seepine
 */
@Slf4j
@RequiredArgsConstructor
public class WeChatTemplate implements InitializingBean {

  private final WeChatProperties weChatProperties;

  @Override
  public void afterPropertiesSet() {
    log.debug("afterPropertiesSet WeChatTemplate");
  }

  /**
   * jsCode转openId
   *
   * @param jsCode 小程序wx.login获得的jsCode
   * @return openId
   * @throws WeChatException exception
   */
  public WxSession jsCode2Session(String jsCode) throws WeChatException {
    try {
      String response =
          HttpUtil.get(
              String.format(
                  ApiConstant.MINI_APP_AUTHORIZATION_CODE_URL,
                  weChatProperties.getAppId(),
                  weChatProperties.getAppSecret(),
                  jsCode));
      JSONObject res = JSONUtil.parseObj(response);
      String openId = res.getStr(KeyConstant.OPENID_KEY);
      String sessionKey = res.getStr(KeyConstant.SESSION_KEY);
      Assert.notEmpty(openId, ErrorConstant.OPEN_ID_ERROR_MESSAGE);
      Assert.notEmpty(sessionKey, ErrorConstant.SESSION_KEY_ERROR_MESSAGE);
      return WxSession.builder().openId(openId).sessionKey(sessionKey).build();
    } catch (Exception e) {
      throw new WeChatException(e.getMessage());
    }
  }

  /**
   * jsCode转openId
   *
   * @param jsCode 小程序wx.login获得的jsCode
   * @return openId
   * @throws WeChatException exception
   */
  public String getOpenId(String jsCode) throws WeChatException {
    WxSession wxSession = jsCode2Session(jsCode);
    return wxSession.getOpenId();
  }

  /**
   * 获取accessToken
   *
   * @return accessToken
   * @throws WeChatException Exception
   */
  public String getAccess() throws WeChatException {
    try {
      String response =
          HttpUtil.get(
              String.format(
                  ApiConstant.ACCESS_URL,
                  weChatProperties.getAppId(),
                  weChatProperties.getAppSecret()));
      JSONObject res = JSONUtil.parseObj(response);
      String access = res.getStr(KeyConstant.ACCESS_TOKEN_KEY);
      Assert.notEmpty(access, ErrorConstant.ACCESS_TOKEN_ERROR_MESSAGE);
      return access;
    } catch (Exception e) {
      throw new WeChatException(e.getMessage());
    }
  }

  /**
   * 解密数据
   *
   * @param sessionKey sessionKey
   * @param encryptedData encryptedData
   * @param iv iv
   * @return WxPhoneBody
   * @throws WeChatException exception
   */
  public WxPhoneBody decrypt(String sessionKey, String encryptedData, String iv)
      throws WeChatException {
    try {
      AES aes =
          new AES(
              Mode.CBC,
              Padding.NoPadding,
              Base64Utils.decodeFromString(sessionKey),
              Base64Utils.decodeFromString(iv));
      String resultByte = aes.decryptStr(Base64Utils.decodeFromString(encryptedData));
      JSONObject jsonObject = JSONUtil.parseObj(resultByte);
      String phoneNumber = jsonObject.getStr("phoneNumber");
      String purePhoneNumber = jsonObject.getStr("purePhoneNumber");
      Integer countryCode = jsonObject.getInt("countryCode");
      Assert.notEmpty(purePhoneNumber, "获取不到purePhoneNumber");
      return WxPhoneBody.builder()
          .phoneNumber(phoneNumber)
          .purePhoneNumber(purePhoneNumber)
          .countryCode(countryCode)
          .build();
    } catch (Exception e) {
      throw new WeChatException(e.getMessage());
    }
  }

  /**
   * 通过jsCode获取手机号
   *
   * @param jsCode jsCode
   * @param encryptedData encryptedData
   * @param iv iv
   * @return 手机号
   * @throws WeChatException exception
   */
  public String getPhone(String jsCode, String encryptedData, String iv) throws WeChatException {
    WxSession wxSession = jsCode2Session(jsCode);
    WxPhoneBody phoneBody = decrypt(wxSession.getSessionKey(), encryptedData, iv);
    return phoneBody.getPurePhoneNumber();
  }

  /**
   * 通过code获取手机号
   *
   * @param code 手机号获取凭证
   * @return 手机号
   * @throws WeChatException exception
   */
  public String getPurePhone(String accessToken, String code) throws WeChatException {
    WxPhoneBody res = getPhone(accessToken, code);
    return res.getPurePhoneNumber();
  }
  /**
   * 通过code获取手机号
   *
   * @param code 手机号获取凭证
   * @return 手机号
   * @throws WeChatException exception
   */
  public WxPhoneBody getPhone(String accessToken, String code) throws WeChatException {
    try {
      JSONObject body = new JSONObject();
      body.set("code", code);
      HttpResponse response =
          HttpUtil.createPost(String.format(ApiConstant.GET_USER_PHONE_NUMBER, accessToken))
              .body(body.toString())
              .execute();
      JSONObject res = JSONUtil.parseObj(response.body());
      if (res.getInt("errcode") > 0) {
        throw new WeChatException(res.getStr("errmsg"));
      }
      JSONObject jsonObject = res.getJSONObject("phone_info");
      String phoneNumber = jsonObject.getStr("phoneNumber");
      String purePhoneNumber = jsonObject.getStr("purePhoneNumber");
      Integer countryCode = jsonObject.getInt("countryCode");
      Assert.notEmpty(purePhoneNumber, "获取不到purePhoneNumber");
      return WxPhoneBody.builder()
          .phoneNumber(phoneNumber)
          .purePhoneNumber(purePhoneNumber)
          .countryCode(countryCode)
          .build();
    } catch (WeChatException e) {
      throw e;
    } catch (Exception e) {
      throw new WeChatException(e.getMessage());
    }
  }
  /**
   * 通过sessionKey解析获取手机号
   *
   * @param sessionKey sessionKey
   * @param encryptedData encryptedData
   * @param iv iv
   * @return 手机号
   * @throws WeChatException exception
   */
  public String getPhoneBySessionKey(String sessionKey, String encryptedData, String iv)
      throws WeChatException {
    WxPhoneBody phoneBody = decrypt(sessionKey, encryptedData, iv);
    return phoneBody.getPurePhoneNumber();
  }

  /**
   * 生成小程序码
   *
   * @param accessToken accessToken
   * @param scene eg:a=1&b=2
   * @param page 页面
   * @return 图片数据
   * @throws WeChatException exception
   */
  public byte[] getUnlimited(String accessToken, String scene, String page) throws WeChatException {
    return getUnlimited(accessToken, WxUnlimitedBody.builder().scene(scene).page(page).build());
  }

  /**
   * 生成小程序码
   *
   * @param accessToken accessToken
   * @param body WxUnlimitedBody
   * @return 图片数据
   * @throws WeChatException exception
   */
  public byte[] getUnlimited(String accessToken, WxUnlimitedBody body) throws WeChatException {
    try {
      HttpResponse response =
          HttpUtil.createPost(String.format(ApiConstant.MINI_APP_GET_UN_LIMITED_CODE, accessToken))
              .body(JSONUtil.toJsonStr(body))
              .execute();
      if (getContentType(response).contains("json")) {
        JSONObject res = JSONUtil.parseObj(response.body());
        throw new WeChatException(res.getInt("errcode"), res.getStr("errmsg"), "生成二维码失败");
      } else {
        return response.bodyBytes();
      }
    } catch (WeChatException e) {
      throw e;
    } catch (Exception e) {
      throw new WeChatException(e.getMessage());
    }
  }

  private String getContentType(HttpResponse response) {
    if (response == null) {
      return StrUtil.EMPTY;
    }
    String contentType = response.header("Content-Type");
    if (StrUtil.isBlank(contentType)) {
      contentType = response.header("content-type");
    }
    if (StrUtil.isBlank(contentType)) {
      contentType = response.header("contentType");
    }
    return contentType == null ? StrUtil.EMPTY : contentType;
  }
  /**
   * 发送消息订阅
   *
   * @param body body
   * @throws WeChatException e
   */
  public void sendSubscribe(String accessToken, WxSubscribeBody body) throws WeChatException {
    try {
      String response =
          HttpUtil.post(
              String.format(ApiConstant.SUBSCRIBE_SEND_URL, accessToken), JSONUtil.toJsonStr(body));
      JSONObject res = JSONUtil.parseObj(response);
      String access = res.getStr(KeyConstant.ACCESS_TOKEN_KEY);
      Assert.notEmpty(access, ErrorConstant.ACCESS_TOKEN_ERROR_MESSAGE);
      if (res.getInt("errcode") > 0) {
        throw new WeChatException(res.getStr("errmsg"));
      }
    } catch (WeChatException e) {
      throw e;
    } catch (Exception e) {
      throw new WeChatException(e.getMessage());
    }
  }

  /**
   * 获取小程序 URL Link
   *
   * @param body body
   * @throws WeChatException e
   */
  public String generateUrlLink(String accessToken, WxUrlLinkBody body) throws WeChatException {
    try {
      String response =
          HttpUtil.post(
              String.format(ApiConstant.URL_LINK_GENERATE, accessToken), JSONUtil.toJsonStr(body));
      JSONObject res = JSONUtil.parseObj(response);
      if (res.getInt("errcode") > 0) {
        throw new WeChatException(res.getStr("errmsg"));
      }
      Assert.notEmpty("url_link", "生成urlLink失败");
      return res.getStr("url_link");
    } catch (WeChatException e) {
      throw e;
    } catch (Exception e) {
      throw new WeChatException(e.getMessage());
    }
  }
}
